package com.chigga.application.filter;

import com.chigga.common.config.properties.JwtProperties;
import com.chigga.common.constants.HttpStatusConstant;
import com.chigga.common.core.domain.ResponseResult;
import com.chigga.common.utils.JwtUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Jwt 过滤器
 *
 * @author DefChigga
 */
@Slf4j
@WebFilter(urlPatterns = "/*") // 拦截所有请求
@Order(1) // 过滤器执行顺序，数值越小越先执行
public class JwtFilter implements Filter {
    /**
     * Jwt 配置属性
     */
    @Resource
    private JwtProperties jwtProperties;

    /**
     * Jwt 工具类
     */
    @Resource
    private JwtUtil jwtUtil;

    /**
     * 请求白名单列表
     */
    private static final List<String> WHITE_REQUEST_LIST = new ArrayList<>(
            List.of("/swagger", "/v3/api-docs", "/webjars", "/register", "/login")
    );

    /**
     * 资源白名单列表
     */
    private static final List<String> WHITE_EXTENSION_LIST = new ArrayList<>(
            List.of(".html", ".css", ".js")
    );

    /**
     * 初始化
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    /**
     * 过滤操作
     *
     * @param servletRequest  servlet 请求
     * @param servletResponse servlet 响应
     * @param filterChain     过滤器链
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String requestUri = request.getRequestURI();

        String token = request.getHeader(jwtProperties.getTokenHeader());
        log.info("请求地址：{}，token 令牌：{}", requestUri, token);
        // 判断白名单
        if (WHITE_REQUEST_LIST.stream().anyMatch(requestUri::contains) || WHITE_EXTENSION_LIST.stream().anyMatch(requestUri::endsWith)) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }
        // 如果没有 token
        if (token == null || token.isEmpty()) {
            writeJsonError(response, ResponseResult.failure(HttpStatusConstant.UNAUTHORIZED, "token 令牌缺失"));
            return;
        }

        // 如果有 token
        try {
            // 去除 Bearer 前缀
            token = token.replace(jwtProperties.getTokenPrefix(), "");
            // 校验 token
            jwtUtil.verifyToken(token);
            // 过滤器放行
            filterChain.doFilter(servletRequest, servletResponse);
        } catch (Exception e) {
            writeJsonError(response, ResponseResult.failure(HttpStatusConstant.UNAUTHORIZED, "token 令牌无效"));
        }
    }

    /**
     * 响应错误信息
     *
     * @param response http 响应
     * @param res      响应错误对象
     */
    private static void writeJsonError(HttpServletResponse response, Object res) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        // 把对象转为 json 字符串
        String jsonRes = objectMapper.writeValueAsString(res);
        // 设置状态码，响应/编码类型
        response.setStatus(HttpStatusConstant.UNAUTHORIZED);
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        // 写出 json 字符串
        response.getWriter().write(jsonRes);
    }

    /**
     * 销毁
     */
    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}
